<?php

namespace StudyBuddy\Controllers;

use StudyBuddy\WebPage;
use StudyBuddy\String;
use StudyBuddy\Mailer;

/**
 * Class responsible for
 * displaying the forgot password
 * form, processing the form,
 * generating a new ramdom password
 * for user
 * and emailing it to user
 */
class Remindpwd extends WebPage
{
	const EMAIL_BODY = 'Hi there, %1$s

You have requested to reset your password on %2$s because you have forgotten your password. 
If you did not request this, please ignore it. 

This link expire and become useless in 24 hours time.

To reset your password, please visit the following page:

%3$s

When you visit that page, your password will be reset, and the new password will be emailed to you.

Your username is: %1$s


All the best,
%2$s

	';

	const TPL_SUCCESS = '<div class="frm1">Your username is <b>%1$s</b>
Please write it down now!
<br/><br/>
Your password reset instructions have been emailed<br/> to the address
associated with your account.</div>';

	const SUBJECT = 'Your password request at %s';

	protected $layoutID = 1;
	
	/**
	 * @var object of type Form
	 */
	protected $oForm;

	protected $aAllowedVars = array('login');


	/**
	 *
	 * Username or email address
	 * submitted in forgotten password form
	 * @var string
	 */
	protected $login;

	/**
	 * User ID of user who forgot password
	 *
	 * @var int
	 */
	protected $uid;


	/**
	 * The user was found by email
	 * and not by username
	 *
	 * @var bool
	 */
	protected $byEmail = false;


	/**
	 *
	 * Email address of user
	 * @var string
	 */
	protected $emailAddress;

	/**
	 *
	 * Secret string will be used in reset
	 * password link, sent to user via email
	 * @var string
	 */
	protected $randomString;


	/**
	 * Remders and processes the form
	 *
	 * @return string page with html form
	 * @param array $arrParams array of GET or POST params
	 */
	protected function main(){
		/**
		 * @todo Translate String
		 * 
		 */
		$this->title = 'Password help';

		$this->oForm = new \StudyBuddy\Forms\Pwd($this->oRegistry);
		$this->aPageVars['title'] = $this->title;


		if($this->oForm->isSubmitted() && $this->oForm->validate() && $this->validateUser()){
			d('cp');
			$this->generateCode()->emailCode();
			$this->aPageVars['body'] = sprintf(self::TPL_SUCCESS, $this->login);
		} else {
			$this->aPageVars['body'] = $this->oForm->getForm();
		}
	}


	/**
	 * Validation to check that user
	 * with this username or email address
	 * exists in the database
	 * If user exists, set the $this->forgottenUid
	 * to the value of this user's id
	 *
	 * @return bool true if user found, otherwise false
	 * and in case of false also sets form errors
	 * so that user will see the form with errors
	 */
	protected function validateUser(){
		$this->login = \mb_strtolower($this->oForm->getSubmittedValue('login'));
		d('$this->login: '.$this->login);
		if (false !== \filter_var($this->login, FILTER_VALIDATE_EMAIL)){
			d('cp');
			$this->byEmail = true;
			$aEmail = $this->oRegistry->Mongo->EMAILS->findOne(array('email' => $this->login));
			if(empty($aEmail)){
				$this->oForm->setError('login', 'No user with this email address');

				return false;
			}

			d('$aEmail: '.print_r($aEmail, 1));
			$aResult = $this->oRegistry->Mongo->USERS->findOne(array('_id' => (int)$aEmail['i_uid']));

		} else {
			if(false === \StudyBuddy\Validate::username($this->login)){
				d('cp');
				$this->oForm->setError('login', 'This username is invalid');
					
				return false;
			}

			$aResult = $this->oRegistry->Mongo->USERS->findOne(array('username_lc' => $this->login));
		}

		if (empty($aResult)) {
			d('cp');
			
			$this->oForm->setError('login', 'User Not found');

			return false;
		}


		/**
		 * @todo
		 *
		 * if 'usertype' == 'email'
		 * then user does not have login
		 * Just test and then throw an exception?
		 * Actually maybe it's better if user could just login
		 * then edit profile and become regular user...
		 *
		 * But how would we do that? We would bacially activate
		 * a user on first login.
		 */
		d('$aResult: '.\print_r($aResult, 1));

		/**
		 * If username exists but email does not
		 * such as the case when user is external user who has
		 * not yet provided email address
		 *
		 */

		if(empty($aResult['email'])){
			throw new \StudyBuddy\Exception('This is an external account and you have not provided a valid email address for it');
		}

		/**
		 * @todo if user does not have username
		 * then we should use email address instead
		 * user should be able to login using email address!
		 *
		 */
		$this->uid = $aResult['_id'];
		$this->login = (!empty($aResult['username'])) ? $aResult['username'] : $aResult['email'];
		$this->emailAddress = $aResult['email'];

		return true;
	}


	/**
	 * Generates a random string
	 * to be use in password reset url
	 * It checks to make sure this string does not already exist
	 * in the PASSWORD_CHANGE table
	 *
	 * @return object $this
	 *
	 * @throws StudyBuddyException in case a unique string
	 * could not be generated
	 */
	protected function generateCode(){
		d('cp');
		$counter = 0;
		$done = false;

		do {
			$counter++;
			$aData = array();
			$aData['_id'] = \strtolower(\StudyBuddy\String::makeRandomString(12));
			$aData['i_ts'] = time();
			$aData['i_uid'] = $this->uid;
				
			/**
			 * @todo
			 * Don't use _id for string,
			 * instead use unique index on string + 'y'/'n' value of 'used'
			 * This way string can be duplicate as long as no same
			 * string is used
			 */
			try {
				$coll = $this->oRegistry->Mongo->PASSWORD_CHANGE;
				$coll->insert($aData, array('fsync' => true));
				$done = true;
				d('cp');
			} catch (\MongoException $e) {
				d('code already exists, trying again...');
			}


		} while (!$done && ($counter < 50) );

		if (!$done) {
			throw new \StudyBuddy\Exception('Error: Unable to generate random string at this time, please try again in 30 seconds');
		}

		$this->randomString = $aData['_id'];

		return $this;
	}


	/**
	 * Prepares the body, subject and from
	 * and email to user
	 *
	 * @todo translate strings instead of using constants
	 * of this class
	 *
	 * @return object $this
	 */
	protected function emailCode(){
		$link = $this->oRegistry->Ini->SITE_URL.'/index.php?a=resetpwd&uid='.$this->uid.'&r='.$this->randomString;
		$body = vsprintf(self::EMAIL_BODY, array($this->login, $this->oRegistry->Ini->SITE_NAME, $link));
		$subject = sprintf(self::SUBJECT, $this->oRegistry->Ini->SITE_NAME);
		
		Mailer::factory($this->oRegistry)->mail($this->emailAddress, $subject, $body);
		
		return $this;
	}

}
